#include "lift_manager.h"

#include <QQueue>
#include <QSet>
#include <QPair>

namespace application {
namespace lift {

LiftManager::LiftManager(std::shared_ptr<kernel::ILogger> logger, QObject* parent)
    : QObject(parent)
    , m_logger(logger)
{
}

void LiftManager::addLift(std::shared_ptr<BaseLift> lift)
{
    if (!lift) {
        if (m_logger) {
            m_logger->warning("尝试添加空电梯对象");
        }
        return;
    }

    int liftNum = lift->getLiftNum();
    if (m_lifts.contains(liftNum)) {
        if (m_logger) {
            m_logger->warning(QString("电梯编号 %1 已存在").arg(liftNum));
        }
        return;
    }

    m_lifts.insert(liftNum, lift);

    if (m_logger) {
        m_logger->info(QString("添加电梯: 编号=%1").arg(liftNum));
    }

    emit liftAdded(liftNum);
}

void LiftManager::removeLift(int liftNum)
{
    if (m_lifts.remove(liftNum) > 0) {
        if (m_logger) {
            m_logger->info(QString("移除电梯: 编号=%1").arg(liftNum));
        }
        emit liftRemoved(liftNum);
    }
}

std::shared_ptr<BaseLift> LiftManager::getLift(int liftNum) const
{
    return m_lifts.value(liftNum, nullptr);
}

std::shared_ptr<BaseLift> LiftManager::requestLift(int liftNum)
{
    auto lift = getLift(liftNum);
    if (!lift) {
        if (m_logger) {
            m_logger->error(QString("电梯 %1 不存在").arg(liftNum));
        }
        return nullptr;
    }

    if (lift->isLocked()) {
        if (m_logger) {
            m_logger->warning(QString("电梯 %1 已被锁定").arg(liftNum));
        }
        return nullptr;
    }

    lift->lock();
    if (m_logger) {
        m_logger->info(QString("请求电梯 %1 成功").arg(liftNum));
    }

    return lift;
}

bool LiftManager::releaseLift(int liftNum)
{
    auto lift = getLift(liftNum);
    if (!lift) {
        return false;
    }

    lift->unlock();
    lift->setStatus(LiftStatus::Idle);

    if (m_logger) {
        m_logger->info(QString("释放电梯 %1").arg(liftNum));
    }

    emit liftRequestCompleted(liftNum);
    return true;
}

FloorInfo LiftManager::getFloorInfo(int liftNum, int floor) const
{
    auto lift = getLift(liftNum);
    if (lift) {
        return lift->getFloorInfo(floor);
    }
    return FloorInfo();
}

LiftRequestInfo LiftManager::getLiftRequestInfo(int startFloor, int targetFloor) const
{
    LiftRequestInfo info;
    info.agvId = -1;

    auto path = findLiftPath(startFloor, targetFloor);
    if (!path.isEmpty()) {
        const auto& segment = path.constFirst();
        info.liftNum = segment.liftNum;
        info.startFloor = segment.startFloor;
        info.targetFloor = segment.targetFloor;
    }

    return info;
}

QVector<LiftSegment> LiftManager::findLiftPath(int startFloor, int targetFloor) const
{
    QVector<LiftSegment> result;

    if (startFloor == targetFloor) {
        return result;
    }

    QHash<int, QList<QPair<int, FloorInfo>>> floorToLiftInfos;
    bool startReachable = false;
    bool targetReachable = false;

    for (auto it = m_lifts.constBegin(); it != m_lifts.constEnd(); ++it) {
        const auto& lift = it.value();
        if (!lift) {
            continue;
        }

        const auto floorInfos = lift->getAllFloorInfos();
        for (const auto& info : floorInfos) {
            floorToLiftInfos[info.floor].append(qMakePair(it.key(), info));
            if (info.floor == startFloor) {
                startReachable = true;
            }
            if (info.floor == targetFloor) {
                targetReachable = true;
            }
        }
    }

    if (!startReachable || !targetReachable) {
        if (m_logger) {
            m_logger->warning(QString("Lift path unavailable: missing floor info (%1 -> %2)")
                                  .arg(startFloor)
                                  .arg(targetFloor));
        }
        return result;
    }

    struct NodeEntry {
        int prevFloor{-1};
        LiftSegment segment;
    };

    QHash<int, NodeEntry> parents;
    QSet<int> visited;
    QQueue<int> queue;

    visited.insert(startFloor);
    queue.enqueue(startFloor);

    while (!queue.isEmpty()) {
        int floor = queue.dequeue();
        if (floor == targetFloor) {
            break;
        }

        const auto liftEntries = floorToLiftInfos.value(floor);
        for (const auto& entry : liftEntries) {
            int liftNum = entry.first;
            const FloorInfo& fromInfo = entry.second;

            const auto liftIt = m_lifts.find(liftNum);
            if (liftIt == m_lifts.end() || !liftIt.value()) {
                continue;
            }

            const auto otherInfos = liftIt.value()->getAllFloorInfos();
            for (const auto& toInfo : otherInfos) {
                if (toInfo.floor == fromInfo.floor) {
                    continue;
                }

                int nextFloor = toInfo.floor;
                if (visited.contains(nextFloor)) {
                    continue;
                }

                LiftSegment segment;
                segment.liftNum = liftNum;
                segment.startFloor = fromInfo;
                segment.targetFloor = toInfo;

                parents.insert(nextFloor, NodeEntry{floor, segment});
                visited.insert(nextFloor);
                queue.enqueue(nextFloor);
            }
        }
    }

    if (!visited.contains(targetFloor)) {
        if (m_logger) {
            m_logger->warning(QString("Lift path unavailable: no route from %1F to %2F")
                                  .arg(startFloor)
                                  .arg(targetFloor));
        }
        return result;
    }

    int currentFloor = targetFloor;
    while (currentFloor != startFloor) {
        if (!parents.contains(currentFloor)) {
            result.clear();
            return result;
        }

        const auto node = parents.value(currentFloor);
        result.prepend(node.segment);
        currentFloor = node.prevFloor;
    }

    return result;
}

QList<std::shared_ptr<BaseLift>> LiftManager::getAllLifts() const
{
    return m_lifts.values();
}

std::shared_ptr<BaseLift> LiftManager::getIdleLift() const
{
    for (auto lift : m_lifts) {
        if (lift->getStatus() == LiftStatus::Idle && !lift->isLocked()) {
            return lift;
        }
    }
    return nullptr;
}

void LiftManager::initializeAllLifts()
{
    if (m_logger) {
        m_logger->info(QString("初始化 %1 个电梯").arg(m_lifts.size()));
    }

    for (auto lift : m_lifts) {
        if (!lift->initialize()) {
            if (m_logger) {
                m_logger->error(QString("电梯 %1 初始化失败").arg(lift->getLiftNum()));
            }
        }
    }
}

} // namespace lift
} // namespace application
